InheritedWidget
是一個具有特殊功能的組件,它提供可以將資料從 widget
從上到下傳遞的功能,達到共享數據的目的,其重要性與 StatelessWidget
和 StatefulWidget
相當。
我們在MaterialApp
可以使用ThemeData
定義App整體的風格,
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
theme: ThemeData(
primaryColor: Colors.green,
accentColor: Colors.yellow[300],
textTheme: const TextTheme(
headline6: TextStyle(fontSize: 24.0, fontStyle: FontStyle.italic),
),
),
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/theme': (context) => ThemeScreen(),
},
);
}
}
在底層的 widget 可以使用 Theme.of(context)
的方法取得上層的共享資料
class ThemeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Button")),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Theme.of(context).accentColor,
child: Text(
'使用來自 MaterialApp 設定的 ThemeData',
style: Theme.of(context).textTheme.headline6,
),
),
],
),
);
}
}
Flutter 習慣使用名稱為 of
的靜態方法,定義 InheritedWidget
功能組件的使用,其應用 BuildContext
的dependOnInheritedWidgetOfExactType
方法從 Element Tree 向父層尋找符合 MyThemeWidget
型態的資源。
讓我們使用 InheritedWidget 實作 MyThemeWidget
,共享自己定義的 MyThemeData
樣式資料。
class MyThemeData {
final Color? primaryColor;
MyThemeData({this.primaryColor});
MyThemeData.fallback() : this(primaryColor: Colors.blue);
}
class MyThemeWidget extends InheritedWidget {
final MyThemeData? data;
MyThemeWidget({required Widget child, this.data}) : super(child: child);
@override
bool updateShouldNotify(MyThemeWidget oldWidget) {
return oldWidget.data != data;
}
static MyThemeData of(BuildContext context) {
final MyThemeWidget? _inheritedTheme =
context.dependOnInheritedWidgetOfExactType<MyThemeWidget>();
MyThemeData theme = _inheritedTheme?.data ?? MyThemeData.fallback();
return theme;
}
}
範例中我們使用 Switch 模擬 MyThemeData 資料變更的狀況
class MyThemeBox extends StatefulWidget {
_MyThemeBoxState createState() => _MyThemeBoxState();
}
class _MyThemeBoxState extends State<MyThemeBox> {
bool toggle = false;
Widget build(BuildContext context) {
var color;
if (toggle) {
color = Colors.red;
} else {
color = Colors.blue;
}
return MyThemeWidget(
data: MyThemeData(primaryColor: color),
child: Container(
child: Column(
children: [
Switch(
value: toggle,
onChanged: (bool value) {
setState(() {
toggle = value;
});
},
),
TextLabel(),
],
),
),
);
}
}
這邊是我們在 MyThemeWidget 底層的組件,可通過of
取得 MyThemeData 資料。
還記得當初在談 State 生命週期的時候有提到 didChangeDependencies
的觸發與是否有使用 InheritedWidget
內的資料有關。
class TextLabel extends StatefulWidget {
_TextLabelState createState() => _TextLabelState();
}
class _TextLabelState extends State<TextLabel> {
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("didChangeDependencies()");
}
@override
Widget build(BuildContext context) {
var primaryColor = MyThemeWidget.of(context).primaryColor;
return Text(
"使用來自 MyThemeWidget 設定的 MyThemeData",
style: TextStyle(color: primaryColor),
);
}
}